/*
 *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */


package com.cloudminds.resttest.service;

import com.cloudminds.resttest.vo.CallParams;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
import java.util.concurrent.CopyOnWriteArraySet;


/**
 * WebSocket client implementation.
 *
 * <p>All public methods should be called from a looper executor thread
 * passed in a constructor, otherwise exception will be thrown.
 * All events are dispatched on the same thread.
 */

public class WebSocketChannelClient {
    private static final String TAG = "HS/WSChannelClient";
    private static final int CLOSE_TIMEOUT = 1000;
    private final WebSocketChannelEvents events;
    private WebSocketClient ws;
    private String wsServerUrl;
    private String regName;
    private String regPwd;
    private String token;
    private String sid;
    JSONObject rdata ;
    private WebSocketConnectionState state;
    private final Object closeEventLock = new Object();
    private boolean closeEvent;
    private static JSONArray answer;
    private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();

    // WebSocket send queue. Messages are added to the queue when WebSocket
    // client is not registered and are consumed in register() call.
    private final LinkedList<String> wsSendQueue;
    private AppRTCClient.AssistantConnectionParameters connectionParameters;

    public String getToken(){
        return token;
    }

    public String getSid(){return sid;}

    /**
     * Possible WebSocket connection states.
     */
    public enum WebSocketConnectionState {
        NEW, CONNECTED, REGISTERED, CLOSED, ERROR
    };


    /**
     * Callback interface for messages delivered on WebSocket.
     * All events are dispatched from a looper executor thread.
     */
    public interface WebSocketChannelEvents {
        public JSONArray onWebSocketMessage(final String message);
        public void onWebSocketClose();
        public void onWebSocketConnectError(final String description);
        public void onWebSocketRegisterError(final String description);
        public void onWebSocketRegisterred(final JSONObject data);
    }

    public void setConnectionParameters(AppRTCClient.AssistantConnectionParameters connectionParameters){
        this.connectionParameters = connectionParameters;
    }

    public WebSocketChannelClient(WebSocketChannelEvents events) {
        this.events = events;
        wsSendQueue = new LinkedList<String>();
        state = WebSocketConnectionState.NEW;
    }

    public WebSocketConnectionState getState() {
        return state;
    }

    public static JSONArray getAnswer(){
        return answer;
    }

    public void connect(final String wsUrl, final String name, final  String password, final String sessionId) {
        checkIfCalledOnValidThread();
        System.out.println("On connect entry!\n");
        if (state != WebSocketConnectionState.NEW&&state!= WebSocketConnectionState.CLOSED) {
            System.out.print("WebSocket is already connected.");
        }
        wsServerUrl = wsUrl;
        regName = name;
        regPwd = password;
        closeEvent = false;

        System.out.println("Connecting WebSocket to: " + wsUrl + ".   regName = "+regName+"\n");
        URI serverUri = null;
        try {
            serverUri = new URI(wsUrl);
        }catch (URISyntaxException e){
            e.printStackTrace();
        }

        ws = new WebSocketClient(serverUri,new Draft_6455()) {
            @Override
            public void onOpen(ServerHandshake handshakedata) {
                System.out.println("WebSocket connection opened to: " + wsServerUrl + "\n");

                state = WebSocketConnectionState.CONNECTED;
                register(regName,regPwd,sessionId);
            }

            @Override
            public void onMessage(final String message) {

                if (state != WebSocketConnectionState.REGISTERED) {

                    try {
                        JSONObject json = new JSONObject(message);
                        String msgId = json.getString("id");

                        if (msgId.equals("loginResponse")) {
                            System.out.println("WSS->C: " + message + "\n");

                            String response = json.getString("response");
                            System.out.println("run: loginResponse "+response);
                            if (response.equals("rejected")) {
                                String detail = json.getString("message");
                                final String errorMessage = "Failed to register: " + detail;
                                System.out.println(errorMessage);
                                if (state != WebSocketConnectionState.ERROR) {
                                    state = WebSocketConnectionState.ERROR;
                                }
                                events.onWebSocketRegisterError(errorMessage);
                            } else {
                                System.out.println("Register to assistant websocket server completed.");
                                state = WebSocketConnectionState.REGISTERED;
                                token = json.getString("token");
                                //webSocketClosing=false;
                                // Send any previously accumulated messages.
                                System.out.println("run: 连接成功");
                                for (String sendMessage : wsSendQueue) {
                                    send(sendMessage);
                                }
                                wsSendQueue.clear();

                                String sessionId = "";
                                boolean isNeedMediaReconnect = false;
                                JSONObject data = null;
                                if (json.has("data")){
                                    data = json.getJSONObject("data");
                                    sid = data.getString("sid");
                                    sessionId = data.getString("sid");
                                    if (data.has("mediaReconnect")){
                                        isNeedMediaReconnect = true;
                                    }
                                }
                                rdata = data;
                              //  events.onWebSocketRegisterred(data);

                                if (isNeedMediaReconnect){
                                    answer =  events.onWebSocketMessage("{\"id\":\"info\",\"type\":\"mediaReconnect\"}");
                                }
                            }
                        }
                    } catch (JSONException e) {
                        System.out.println("WebSocket message JSON received from HariServer parsing error: " + e.toString());
                    }
                }

                if (state == WebSocketConnectionState.CONNECTED
                        || state == WebSocketConnectionState.REGISTERED) {
                    answer =  events.onWebSocketMessage(message);
                }
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                System.out.println("WebSocket connection closed. Code: " + code
                        + ". Reason: " + reason + ". State: " + state + " rcuId: " + connectionParameters.username);
                //断网CODE=5
                synchronized (closeEventLock) {
                    closeEvent = true;
                    closeEventLock.notify();
                }

                if (state != WebSocketConnectionState.CLOSED) {
                    state = WebSocketConnectionState.CLOSED;
                    events.onWebSocketClose();
                }
            }

            @Override
            public void onError(Exception ex) {

            }
        };

        ws.setConnectionLostTimeout(600);
        try {
            System.out.println("WebSocket connect...");
            ws.connect();
        } catch (Exception e) {
            final String errorMessage = "WebSocket connection error: " + e.getMessage();
            System.out.println(errorMessage);

            if (state != WebSocketConnectionState.ERROR) {
                state = WebSocketConnectionState.ERROR;
                events.onWebSocketConnectError(errorMessage);
            }
        }
        System.out.print("===============sid="+sid+"data"+rdata);
        /*Map<Object, Object> map1 = new HashMap<Object, Object>();
        map1.put("sid",sid);
        map1.put("data",rdata);*/
    }

    public void rebind(final String sessionId)
    {
        System.out.println("Rebind WebSocket ... with username: " + regName);

        state = WebSocketConnectionState.CONNECTED;
        register(regName, regPwd,sessionId);
    }

    public void register(final String regName, final String pwd, final String sessionId) {
        checkIfCalledOnValidThread();
        this.regName = regName;
        if (state != WebSocketConnectionState.CONNECTED) {
            System.out.println("WebSocket register() in state " + state);
            return;
        }
        System.out.println("Registering name " + regName + ".");
        JSONObject json = new JSONObject();
        JSONObject data = new JSONObject();
        try {
            json.put("id", "login");
            json.put("name", regName);//regName+"#"+PreferenceUtils.getPrefString(BaseConstants.PRE_KEY_TENANTID,"10086")
            if(!sessionId.isEmpty())
                json.put("sid", sessionId);
            json.put("role", "client");
            json.put("password", pwd);
            json.put("appType", "pepper");

            data.put("tenantId",CallParams.TENANT_ID);
            data.put("rcuId", connectionParameters.rcuID);
            data.put("lang", "CH");
            data.put("robotId",connectionParameters.rcuID);
            data.put("rcuFirstConnection", false);
            data.put("rcuFirstConnectionDate", String.valueOf(System.currentTimeMillis()));
            json.put("data",data);
        } catch (JSONException e) {
            System.out.println("WebSocket register JSON error: " + e.getMessage());
            return;
        }

        System.out.println("C->WSS: " + json.toString());
        ws.send(json.toString());

    }


    public void send(String message) {
        System.out.print("==============="+state);
        checkIfCalledOnValidThread();
        switch (state) {
            case NEW:
            case CONNECTED:
            // Store outgoing messages and send them after websocket client
            // is registered.
            //LogUtils.d(TAG, "WS ACC: " + message);
                wsSendQueue.add(message);
                return;
            case ERROR:
            case CLOSED:
                //LogUtils.e(TAG, "WebSocket send() in error or closed state : " + message);
                return;
            case REGISTERED:
                //LogUtils.d(TAG, "C->WSS: " + message);
                ws.send(message);
                break;
        }
        return;
    }


  public void disconnect(boolean waitForComplete) {
    checkIfCalledOnValidThread();
    System.out.println("Disonnect WebSocket. State: " + state);

    // Close WebSocket in CONNECTED or ERROR states only.
    if (state == WebSocketConnectionState.CONNECTED
        || state == WebSocketConnectionState.REGISTERED
        || state == WebSocketConnectionState.ERROR) {

      if(ws!=null)
            ws.close();
      state = WebSocketConnectionState.CLOSED;

      // Wait for websocket close event to prevent websocket library from
      // sending any pending messages to deleted looper thread.
      if (waitForComplete) {
        synchronized (closeEventLock) {
          while (!closeEvent) {
            try {
              closeEventLock.wait(CLOSE_TIMEOUT);
              break;
            } catch (InterruptedException e) {
              System.out.println("Wait error: " + e.toString());
            }
          }
        }
      }
    }
    System.out.println("Disonnecting WebSocket done.");
  }

//  private void reportError(final String errorMessage) {
//    Log.e(TAG, errorMessage);
//    executor.execute(new Runnable() {
//      @Override
//      public void run() {
//        if (state != WebSocketConnectionState.ERROR) {
//          state = WebSocketConnectionState.ERROR;
//          events.onWebSocketError(errorMessage);
//        }
//      }
//    });
//  }

   // Helper method for debugging purposes. Ensures that WebSocket method is
   // called on a looper thread.
  private void checkIfCalledOnValidThread() {

  }
}
